﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Threading;
using RbmlVisualizer.Data;
using RbmlVisualizer.Parser;
using RbmlVisualizer.DiagramComparer;
using RbmlVisualizer.Data.Blocks;

namespace RbmlVisualizer
{
    public class MainWindowViewModel : ViewModelBase
    {
        private int pendingUMLImageRequestCount = 0;
        private int pendingRBMLImageRequestCount = 0;

        public MainWindowViewModel()
        {
            this.UMLItems = new ObservableCollection<DiagramFile>();
            this.RBMLItems = new ObservableCollection<DiagramFile>();

            this.AddItemsToUMLBox();
            this.AddItemsToRBMLBox();
            this.RunAlgorithm();
        }

        public bool PassOrFail { get; set; }
        public double Score { get; set; }

        public ObservableCollection<DiagramFile> RBMLItems { get; set; }
        public ObservableCollection<DiagramFile> UMLItems { get; set; }

        public string UMLImageFilePath { get; set; }
        public string RBMLImageFilePath { get; set; }

        private DiagramFile _umlSelectedItem;
        public DiagramFile UMLSelectedItem
        {
            get { return _umlSelectedItem; }
            set
            {
                if (_umlSelectedItem == value)
                    return;

                _umlSelectedItem = value;

                if (value.ImageFilePath == null && value.DiagramFileNameOnly != null)
                {
                    this.UMLImageFilePath = null;
                    OnPropertyChanged("UMLImageFilePath");
                    this.RunPlantUMLToGenerateImageFile(value);
                }
                else
                {
                    this.UMLImageFilePath = _umlSelectedItem.ImageFilePath;
                    OnPropertyChanged("UMLImageFilePath");
                }

                this.RunAlgorithm();
            }
        }

        private DiagramFile _rbmlSelectedItem;
        public DiagramFile RBMLSelectedItem
        {
            get { return _rbmlSelectedItem; }
            set
            {
                if (_rbmlSelectedItem == value)
                    return;


                _rbmlSelectedItem = value;

                if (value.ImageFilePath == null && value.DiagramFileNameOnly != null)
                {
                    this.RBMLImageFilePath = null;
                    OnPropertyChanged("RBMLImageFilePath");
                    this.RunPlantUMLToGenerateImageFile(value);
                }
                else
                {
                    this.RBMLImageFilePath = _rbmlSelectedItem.ImageFilePath;
                    OnPropertyChanged("RBMLImageFilePath");
                }

                this.RunAlgorithm();
            }
        }

        private void RunAlgorithm()
        {

            ClassDiagram umlDiagram;
            ClassDiagram rbmlDiagram;
            RBMLTextParser rbmlParser = new RBMLTextParser();
            PlantUmlTextParser plantUMLParser = new PlantUmlTextParser();

            // Sanity null check -- this should never happen
            if (this.RBMLSelectedItem == null || this.UMLSelectedItem == null)
                return;


            Logging.LoggingService.Instance.Clear();

            umlDiagram = plantUMLParser.ParseDiagramFromFile(new Uri(UMLSelectedItem.DiagramFilePath));
            rbmlDiagram = rbmlParser.ParseDiagramFromFile(new Uri(RBMLSelectedItem.DiagramFilePath));

            if (umlDiagram.Entities.Count == 0 || rbmlDiagram.Entities.Count == 0)
                Logging.LoggingService.Instance.WriteError("One of the diagrams produced no entities. UML Entities:{0} RBML Entities:{1}, UML Links:{2}, RBML Links:{3}", umlDiagram.Entities.Count, rbmlDiagram.Entities.Count, umlDiagram.Links.Count, rbmlDiagram.Links.Count);
            else
                Logging.LoggingService.Instance.WriteInfo("Parsed {0} UML entities, {1} links and {2} RBML entities, {3} links", umlDiagram.Entities.Count, umlDiagram.Links.Count, rbmlDiagram.Entities.Count, rbmlDiagram.Links.Count);

            List<ClassifierBlock> umlClassifierBlocks = DiagramConverter.Instance.ConvertToClassifierBlocks(umlDiagram);
            List<ClassifierBlock> rbmlClassifierBlocks = DiagramConverter.Instance.ConvertToClassifierBlocks(rbmlDiagram);
            if (umlClassifierBlocks.Count == 0 || rbmlClassifierBlocks.Count == 0)
                Logging.LoggingService.Instance.WriteError("The converter failed to produce blocks, check your diagram. UML:{0} RBML:{1}", umlClassifierBlocks.Count, rbmlClassifierBlocks.Count);
            else
                Logging.LoggingService.Instance.WriteInfo("Converted entities to blocks.  Results are UML:{0} RBML:{1}", umlClassifierBlocks.Count, rbmlClassifierBlocks.Count);


            DiagramComparer.DiagramComparer diagramComparer = new DiagramComparer.DiagramComparer();
            this.PassOrFail = diagramComparer.CheckConformance(umlClassifierBlocks, rbmlClassifierBlocks);
            this.Score = Math.Round(diagramComparer.Score, 2);

            OnPropertyChanged("PassOrFail");
            OnPropertyChanged("Score");
        }

        private void AddItemsToUMLBox()
        {
            string[] filePaths = Directory.GetFiles(Directory.GetCurrentDirectory() + "\\DefaultDiagrams\\UML", "*.txt");
            foreach (var filePath in filePaths)
            {
                string imageFile = Directory.GetCurrentDirectory() + "\\DefaultDiagrams\\UML\\img\\" + filePath.Replace(filePath, System.IO.Path.GetFileNameWithoutExtension(filePath) + ".png");

                this.UMLItems.Add(new DiagramFile()
                {
                    Content = System.IO.Path.GetFileNameWithoutExtension(filePath),
                    DiagramFilePath = filePath,
                    FileType = RbmlVisualizer.FileType.UML,
                    ImageFilePath = (File.Exists(imageFile)) ? imageFile : null
                });
            }
        }

        private void AddItemsToRBMLBox()
        {
            string[] filePaths = Directory.GetFiles(Directory.GetCurrentDirectory() + "\\DefaultDiagrams\\RBML", "*.txt");
            foreach (var filePath in filePaths)
            {
                string imageFile = Directory.GetCurrentDirectory() + "\\DefaultDiagrams\\RBML\\img\\" + filePath.Replace(filePath, System.IO.Path.GetFileNameWithoutExtension(filePath) + ".png");

                this.RBMLItems.Add(new DiagramFile()
                {
                    Content = System.IO.Path.GetFileNameWithoutExtension(filePath),
                    DiagramFilePath = filePath,
                    FileType = RbmlVisualizer.FileType.RBML,
                    ImageFilePath = (File.Exists(imageFile)) ? imageFile : null
                });
            }
        }

        private void RunPlantUMLToGenerateImageFile(DiagramFile diagramFile)
        {
            Thread plantUMLImageCreator = new Thread(new ParameterizedThreadStart(this.CreateImage));
            plantUMLImageCreator.Start(diagramFile);
        }

        private void CreateImage(object data)
        {
            DiagramFile df = data as DiagramFile;
            string plantUmlJar = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Thirdparty\\plantuml.jar");
            string plantUmlPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Thirdparty\\plantuml.exe");

            // Use plantuml to generate the graph    
            this.RecordPendingRequest(df);
            using (var process = new Process())
            {
                var startInfo = new ProcessStartInfo();
               // startInfo.FileName = "java.exe";
                startInfo.FileName = plantUmlPath;
                //  startInfo.Arguments = "-jar " + plantUmlJar + " -graphvizdot \"" + Settings.Default.GraphVizLocation + "\" \"" + df.DiagramFilePath + "\"";
               //   startInfo.Arguments = "-jar " + plantUmlJar + " " + df.DiagramFilePath;
                startInfo.Arguments = df.DiagramFilePath;
                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
                process.StartInfo = startInfo;
                if (process.Start())
                {
                    process.WaitForExit(10000);
                }

                //Adding a kill so it doesn't bog down your computer :). 
               // process.Kill(); 
            }
            this.FinishRequest(df);

            df.ImageFilePath = Directory.GetParent(df.DiagramFilePath) + "\\img\\" + System.IO.Path.GetFileName(df.DiagramFilePath.Replace(df.DiagramFileNameOnly, System.IO.Path.GetFileNameWithoutExtension(df.DiagramFilePath) + ".png"));

            if (df.FileType == FileType.RBML)
            {
                this.RBMLImageFilePath = df.ImageFilePath;
                OnPropertyChanged("RBMLImageFilePath");
            }
            else if (df.FileType == FileType.UML)
            {
                this.UMLImageFilePath = df.ImageFilePath;
                OnPropertyChanged("UMLImageFilePath");
            }
        }

        // TODO - low priority - move these out to the ViewModelBase and genericize them. 
        private void RecordPendingRequest(DiagramFile diagramFile)
        {
            if (diagramFile.FileType == FileType.UML)
            {
                int newRequestCount = Interlocked.Increment(ref this.pendingUMLImageRequestCount);
                if (newRequestCount == 1)
                {
                    this.OnPropertyChanged("UMLImageIsLoading");
                }
            }
            else if (diagramFile.FileType == FileType.RBML)
            {
                int newRequestCount = Interlocked.Increment(ref this.pendingRBMLImageRequestCount);
                if (newRequestCount == 1)
                {
                    this.OnPropertyChanged("RBMLImageIsLoading");
                }
            }
        }

        private void FinishRequest(DiagramFile diagramFile)
        {
            if (diagramFile.FileType == FileType.UML)
            {
                int newRequestCount = Interlocked.Decrement(ref this.pendingUMLImageRequestCount);
                if (newRequestCount == 0)
                {
                    this.OnPropertyChanged("UMLImageIsLoading");
                }
            }
            if (diagramFile.FileType == FileType.RBML)
            {
                int newRequestCount = Interlocked.Decrement(ref this.pendingRBMLImageRequestCount);
                if (newRequestCount == 0)
                {
                    this.OnPropertyChanged("RBMLImageIsLoading");
                }
            }

        }

        public bool RBMLImageIsLoading
        {
            get { return this.pendingRBMLImageRequestCount > 0; }
        }

        public bool UMLImageIsLoading
        {
            get { return this.pendingUMLImageRequestCount > 0; }
        }
    }
}
